package main

import (
	"errors"
	"fmt"
	"math/cmplx"
	"reflect"
	"time"

	"golang.org/x/text/encoding/simplifiedchinese" //用于字符转码的库
)

func main() {
	// fmt.Println("Hello world!")
	study()
}

func study() {
	fmt.Printf("默认格式的值:%v, Go 样式的值:%#v, Go类型:%T \n", [...]int{1}, [...]int{2}, [...]int{3}) //常用打印

	// 变量新建
	var a int    //新建了初始值为0的 int a
	var s string //新建了初始值为"" 的stirng
	fmt.Printf("a 的初始值是 %v \n", a)
	fmt.Printf("s 的初始值是 %v \n", s)

	//多个变量 跟据右边表达式新建并初始化
	a, b := 1, 2
	fmt.Printf("a 的值是 %v , b的值为 %v\n", a, b)

	//b, _ := 3, 4	//左边表达式没有新的变量 所以会报错 no new variables on left side of :=
	b, _ = 3, 4 // _ 用于丢掉右边表达式的值
	fmt.Printf("b 的值是 %v \n", b)

	//一些常见的数值类型
	var i8 int8
	var i16 int16
	var i32 int32
	var i64 int64

	var b8 byte
	var ui8 uint8
	var ui16 uint16
	var ui32 uint32
	var ui64 uint64

	var f32 float32
	var f64 float64

	fmt.Println(reflect.TypeOf(i8))
	fmt.Println(reflect.TypeOf(i16))
	fmt.Println(reflect.TypeOf(i32))
	fmt.Println(reflect.TypeOf(i64))

	fmt.Println(reflect.TypeOf(b8))
	fmt.Println(reflect.TypeOf(ui8))
	fmt.Println(reflect.TypeOf(ui16))
	fmt.Println(reflect.TypeOf(ui32))
	fmt.Println(reflect.TypeOf(ui64))

	fmt.Println(reflect.TypeOf(f32))
	fmt.Println(reflect.TypeOf(f64))

	const (
		enum_a = iota
		enum_b
		enum_s = "enum"
	)

	fmt.Printf(
		"enum_a = %v, enum_b = %v, enum_s = %v, \n",
		enum_a, enum_b, enum_s)

	// 3.14					// 十进制，合法
	// 0213				// 八进制，合法
	// 0x4b				// 十六进制，合法
	fmt.Printf(
		" %v 0%o 0x%08X %v \n",
		3.14, 0213, 0xFEE, 4+2i)

	//字符串
	str := "this is a string"
	fmt.Println(str)

	//像c语言修改字符串
	carr := []int32(str) // 转换成整型切片 rune 是int32的别名 也可以写成 carr := []rune(str)
	fmt.Printf("carr[0] = %v \n", carr[0])
	fmt.Printf("carr[0] type is %v \n", reflect.TypeOf(carr[0]))
	carr[0] = '_'
	fmt.Printf("carr[0] = %v \n", carr[0])
	str = string(carr) //转换回字符串
	fmt.Println(str)

	//多行字符串
	str =
		`
	line1
	line2
	`
	fmt.Println(str)

	//中文字符串编码转换
	// const (
	// 	UTF8    = "UTF-8"
	// 	GB18030 = "GB18030"
	// 	GBK     = "GBK"
	// )

	decodeStr, _ := simplifiedchinese.GB18030.NewEncoder().String("中文") // 将字符串从UTF-8编码成GB18030输出
	fmt.Println(decodeStr)
	fmt.Println([]byte(decodeStr))
	encodeByte, _ := simplifiedchinese.GB18030.NewDecoder().Bytes([]byte(decodeStr)) // 将字符串从GB18030解码成UTF-8输出
	fmt.Println(string(encodeByte))

	decodeStr, _ = simplifiedchinese.GBK.NewEncoder().String("中文") // 将字符串从UTF-8编码成GBK输出
	fmt.Println(decodeStr)
	fmt.Println([]byte(decodeStr))
	encodeByte, _ = simplifiedchinese.GBK.NewDecoder().Bytes([]byte(decodeStr)) // 将字符串从GBK解码成UTF-8输出
	fmt.Println(string(encodeByte))

	//复数
	var c complex64 = 1 + 2i

	fmt.Printf("Value is: %v ,real is %v,imag is %v , magnitude is %v \n", c, real(c), imag(c), cmplx.Abs(complex128(c)))

	//控制语句
	if true || false {
		fmt.Println("true || false")
	} else {
		fmt.Println("true || false")
	}

	if cond1, cond2 := 1, 2; cond2 > cond1 { //带初始化的
		fmt.Println("cond1, cond2 := 1, 2")
	}

	// 注意第一个大括号要与if 同行，否则是错误的语法
	// 	if 1 != nil
	// {   //必须同if 在同一行
	// 	fmt.Println("cond1, cond2 := 1, 2")
	// }

	_ = sserr()

	//goto 示例
	// Here:   // 这行的第一个词，以分号结束作为标签 标签名是大小写敏感的。
	// println(i)
	// i++
	// goto Here   //跳转

	//for 循环
	sum := 0
	for i := 0; i < 10; i++ { //像c的用法
		sum = sum + 1
	}
	fmt.Printf("sum = %v \n", sum)

	for 1 > 0 { //go 里没有while 像 while(){} 的for循环
		fmt.Println("像 while(){} 的for循环")
		break
	}

	for { //死循环
		fmt.Println("for 死循环")
		break
	}

	//带标签的 break
J:
	for j := 0; j < 5; j++ {
		for i := 0; i < 10; i++ {
			if i > 5 {
				fmt.Println("带标签的break")
				break J //现在终止的是j 循环，而不是i 的那个
			}
			println(i)
		}
	}

	//range的使用
	list := []string{"a", "b", "c", "d", "e", "f"}
	for k, v := range list { //切片 返回索引和值
		fmt.Printf("key = %v , v = %v \n", k, v)
		break
	}

	for pos, char := range "a中x" { //range  直接作用在 string 返回索引和int32的 k v对
		//fmt.Println(reflect.TypeOf(char))
		fmt.Printf("character '%c' starts at byte position %d\n", char, pos)
	}

	countryCapitalMap := map[string]string{"France": "Paris", "Italy": "Rome", "Japan": "Tokyo", "India": "New delhi"}
	for k, v := range countryCapitalMap { //range 作用到map 返回 k v对
		fmt.Printf("key = %v , v = %v \n", k, v)
		break
	}

	//switch语句

	switch { // 匹配第一 case true
	case 1 > 2:
		fmt.Println("1>2")
	case 1 < 2:
		fmt.Println("1<2")
	case 1 < 2:
		fmt.Println("1<2 11")
	}

	switch 0 { //默认不会像c那样连续执行
	case 0:
		fmt.Println("switch no fallthrough")
	case 1:
		fmt.Println("非连续执行")
	}

	switch 0 { //通过fallthrough指定像c语言那样连接执行
	case 0:
		fmt.Println("switch fallthrough")
		fallthrough
	case 1:
		fmt.Println("连续执行1")
		fallthrough
	case 2:
		fmt.Println("连续执行2")
	case 3:
		fmt.Println("没有 fallthrough")
	}

	switch '=' { //case可以使用逗号分隔的列表
	case ' ', '?', '&', '=', '#', '+': // 效果就像是 或 运算
		fmt.Println("switch 逗号分隔 case")
	}

	//数组
	arr := [...]int{1, 2, 3} //或都这样 arr := [3]int{1, 2, 3}  应该相当于是定长的slice
	fmt.Println(reflect.TypeOf(arr))
	fmt.Println(arr)
	arr[0] = 4
	fmt.Println(arr)

	//arr = [...][2]int{{1, 2}, {3, 4}}   //报错 ， 数组定义了就不能修改数组维度进行赋值
	arr1 := [...][2]int{{1, 2}, {3, 4}} //多维数组
	fmt.Println(reflect.TypeOf(arr1))
	fmt.Println(arr1)

	//切片
	sl1 := arr[:3] //从数组 [0,3) 生成切片 不append相当于是数组的指针
	fmt.Println(reflect.TypeOf(sl1))
	fmt.Println(sl1)
	sl1[0] = 0
	fmt.Println(sl1)
	fmt.Println(arr)
	fmt.Printf("len()返回切片长度 %v \n", len(sl1))
	fmt.Printf("cap()返回切片指向数组的长度 %v\n", cap(sl1))

	sl1 = make([]int, 10) //这里新建了slice ，初始存放了10元素
	fmt.Println(sl1)
	fmt.Println(arr)

	arr2 := [...]int{1, 2, 3, 5, 6, 7}
	sl2 := arr2[1:2] //从数组 [1,2) 生成切片 不append相当于是数组的指针
	fmt.Printf("len()返回切片长度 %v \n", len(sl2))
	fmt.Printf("cap()返回切片指向数组的长度 %v\n", cap(sl2))

	//从数组生成切片，或切片生成切片
	sl4 := arr2[2:4] //从序号2至3创建slice，它包含元素3, 5；
	sl5 := sl4[:]    //从slice s2创建slice，注意s5仍然指向array a。
	fmt.Println(sl4)
	fmt.Println(sl5)

	//扩展切片元素
	//sl4[6] = 7 //这里会报错，因为超出了已有的元素个数 panic: runtime error: index out of range [6] with length 2
	sl4 = append(sl4, 111, 222, 3, 4, 5)
	fmt.Println(sl4)
	fmt.Println(arr2)         //未超出指向原始数组时会修改数组，超出后不会修数组而是新建一个内存放置slice，
	sl4 = append(sl4, sl5...) //用其它切片扩展
	fmt.Println(sl4)

	//切片元素制
	var arr4 = [...]int{0, 1, 2, 3, 4, 5, 6, 7}
	var sl6 = make([]int, 6)
	n1 := copy(sl6, arr4[0:]) //n1 == 6, s == []int{0, 1, 2, 3, 4, 5}
	fmt.Printf("1复制了 %v 个元素到切片\n", n1)
	fmt.Println(sl6)
	n2 := copy(sl6, sl6[2:]) //n2 == 4, s == []int{2, 3, 4, 5, 4, 5} 只复制了前面4个
	fmt.Printf("2复制了 %v 个元素到切片\n", n2)
	fmt.Println(sl6)

	//map
	weekmap := make(map[string]int)
	weekmap["Mon"] = 0 //插入元素
	weekmap["Mon"] = 1 //修改元素
	weekmap["Tua"] = 2
	weekmap["Del"] = 3
	fmt.Println(weekmap)
	delete(weekmap, "Del") //删除元素
	fmt.Println(weekmap)

	//检查map 中是否有元素
	println(weekmap["Tua"])
	mapvalue, present := weekmap["Del"]
	fmt.Printf("Del 存在 = %v , 值为 %v \n", present, mapvalue)

	//命名 的 函数 返回参数
	iii1, jjj1, errr1 := nameretparam()
	fmt.Printf("命名返回参数 i = %v , j = %v , err = %v \n ", iii1, jjj1, errr1)

	//defer
	iii1 = deferfunc()
	fmt.Printf(" 虽然 有'return 1' 但实际返回是defer后的值 : %v \n ", iii1)

	//变参函数
	unknowinputlen(4, 6, 8, 9)

	//函数作为变量
	funcasvar()
	/*
		将整数转换函数变量可以这样写
		var xs = map[int]func() int{
		    1: func() int { return 10 },
			2: func() int { return 20 },
			3: func() int { return 30 },   //必须有逗号
		....
		}
	*/

	//回调函数
	callback(func() {
		fmt.Println("这里是回调函数")
	})

	//panic recover机制
	haspanic := throwsPanic(func() {
		panic("panic test")
	})
	fmt.Printf("throwsPanic 是否发现 panic? : %v \n", haspanic)

	//指针
	var pointer *int32
	fmt.Println("指针的初始化值为 空 ", pointer)
	pointer = &iii1 //指向实际的变量
	// pointer = pointer + 1	//go 不支持指针运算
	fmt.Println("变量值", iii1, ",指针值", pointer, ",指针指向的内存值", *pointer)

	coustomt := new(NameAge)
	fmt.Printf("new 返回的是指针类型 %T \n", coustomt)
	coustomt1 := NameAge{"user", 100}
	fmt.Printf("直接新建变量 返回的是类型 %T \n", coustomt1)
	fmt.Println("自定义类型的名称成员 ", coustomt1.name, ",自定义类型的年龄成员 ", coustomt1.age)
	coustomt1.print()

	//接口
	var nameageI IPersonInfo
	// nameageI = coustomt1 //由于实现NameAge的 IPersonInfo函数集时，用的是指针 ，所以会报错 不能给接口引用 直接赋值变量
	nameageI = IPersonInfo(coustomt) //接口引用 只能赋值指针
	nameageI.set("user1", 1)

	// swefwfwe := S{}
	// var iiiiiii I
	// iiiiiii = swefwfwe	 //实现接口时，不是用指针指定的 这样是可行的
	// iiiiiii = &swefwfwe //实现接口时，不是用指针指定的 这样是可行的
	// println(iiiiiii)

	fmt.Println("接口调用 ", nameageI.getname(), " ", nameageI.getage())
	//通过switch 判断接口实际的类型
	showItype(coustomt)

	//判断接口变量是否获取到实现了接口实体
	vartype, impiperson := nameageI.(IPersonInfo)
	//vartype := nameageI.(IPersonInfo) //这样也是可以的
	if impiperson {
		fmt.Printf("nameageI 接口引用 引用了类型为 %v 的变量\n", vartype)
	}

	//空接口的利用
	fmt.Println("emptyInterfactest(1)调用前")
	emptyInterfactest(1)
	fmt.Println("看吧 emptyInterfactest(1)调用后 出错了 调用recover了")
	emptyInterfactest(coustomt)

	//类型 扩展 方法
	var expinifoo Foo
	expinifoo = 3
	expinifoo.fooMethod()

	//类型 种类
	fmt.Println("1.313 的类型种类是 ", reflect.TypeOf(1.313).Kind())
	fmt.Println("expinifoo 的类型种类是 ", reflect.TypeOf(expinifoo).Kind())
	fmt.Println("[...]int{1,2,3} 的类型种类是 ", reflect.TypeOf([...]int{1, 2, 3}).Kind())
	fmt.Println("[]int{1,2,3} 的类型种类是 ", reflect.TypeOf([]int{1, 2, 3}).Kind())
	fmt.Println("map[string]int{'1':1} 的类型种类是 ", reflect.TypeOf(map[string]int{"1": 1}).Kind())
	fmt.Println("new(int) 的类型种类是指针 ", reflect.TypeOf(new(int)).Kind()) //这里可以分辨出来是指针

	//反射显示字段的标签
	type person struct {
		Name string `name:"namestr"` //"namestr" 是标签 新版Go好像要加上json格式 键值对
		//name string `name:"namestr"` //没有导出的字段，不能利用reflect修改值否则会 panic
		age int
	}
	pppp := new(person)
	pppp.Name = "personname"
	pppp.age = 3
	fmt.Println(reflect.TypeOf(pppp).Elem().Field(0).Tag)

	//反射类型和值
	showref := func(i interface{}) {
		switch t := i.(type) {
		case person:
			// fmt.Println(reflect.TypeOf(i).Elem())	//报错 由于i 不是指针， Elem()返回的是 (*i)的类型
			fmt.Printf("i 的种类型是 %v \n", reflect.TypeOf(i).Kind())
		case *person:
			fmt.Println(t)
			t1 := reflect.TypeOf(i) //得到实际类型的元数据
			v := reflect.ValueOf(i) //得到的值
			elem := t1.Elem()
			fmt.Printf("(*i) 的类型是 %v \n", elem)
			tag := t1.Elem().Field(0).Tag      //元素集字段0的标签
			name := v.Elem().Field(0).String() //元素集字段0的字符串值
			fmt.Printf("i 的实际类型是 %v, 值是 %v, 元素集字段0 标签是 %v, 元素集字段0 字符串值是 %v\n", t1, v, tag, name)

			v.Elem().Field(0).SetString("modify name") //只能修改 导出的字段 否则panic
			fmt.Println("修改 元素集字段0 字符串值 改为：", v)
		}
	}
	showref(pppp)
	showref(person{"", 1})

	//goroutine 并行函数
	concurrentfunc := func(w string, sec int) {
		time.Sleep(time.Duration(sec) * time.Second) //time.Duration()是转化为int64位数，乘以time.Second是转化为秒
		fmt.Println(w, " is ready!")
	}

	go concurrentfunc("goroutine1", 2)
	go concurrentfunc("goroutine2", 1)
	time.Sleep(3 * time.Second)

	//channel 并行函数间的通信
	chanvar := make(chan int)
	// chanvar := make(chan int,4)  //创建了可以存储4 个元素的int 型channel,前4 个元素可以无阻塞的写入

	concurrentfunc1 := func(w string, sec int, c *chan int) {
		time.Sleep(time.Duration(sec) * time.Second) //time.Duration()是转化为int64位数，乘以time.Second是转化为秒
		fmt.Println(w, " is ready!")
		*c <- sec //发送数据到channel
	}

	go concurrentfunc1("goroutine3", 2, &chanvar)
	go concurrentfunc1("goroutine4", 1, &chanvar)

	// close(chanvar) //可以关闭管道
	// <-chanvar //监听channel 直到获取到数据
	// <-chanvar

	//select
	//select 是 Go 中的一个控制结构，类似于用于通信的 switch 语句。每个 case 必须是一个通信操作，要么是发送要么是接收。
	a = 0
waitchan:
	for {
		select {
		case <-chanvar:
			a = a + 1
			if a > 1 {
				break waitchan //必须有这个标签，否则只是break select，没有break for ，导致所有goroutine asleep panic
			}

		}
	}

	// _, ischanclose := <-chanvar
	// fmt.Printf("chanvar 是否已经关闭 ? %v \n", ischanclose)

	//channel 超时
	func() {
		ch1 := make(chan string, 10)
		ch2 := make(chan string, 10)
		fmt.Println("进入到测试 channel超时实现方法")
		select { //没有写任何数据，然后使用了`select `去监控两个`channel`
		case c1 := <-ch1:
			fmt.Println("data from channel #1", c1)
		case c2 := <-ch2:
			fmt.Println("data from channel #2", c2)
		// 如果把这个注释掉，则会阻塞 deadlock
		case <-time.After(5 * time.Second):
			fmt.Println("收到 time.After 信号，超时了")
			break
		}

		fmt.Println("select退出了，退出函数")
	}()

	//通过 chan 停止协程
	func() {
		done := make(chan bool)
		go func() {
			for {
				fmt.Println("监控01...")
				time.Sleep(1 * time.Second)

				select {
				case <-done:
					fmt.Println("退出携程01")
					return
				default:
					continue
				}
			}
		}()

		go func() {
			for res := range done { //没有消息阻塞状态，chan关闭 for 循环结束
				fmt.Println(res) //这里没有取到值，所以一直不会执行
			}
			fmt.Println("退出监控03")
		}()

		go func() {
			for {
				fmt.Println("监控02...")
				time.Sleep(1 * time.Second)

				select {
				case <-done:
					fmt.Println("退出携程02")
					return
				default:
					continue
				}
			}
		}()
		time.Sleep(3 * time.Second)
		// done <- true //这样可以让 for range chan 里面执行
		// done <- true
		// done <- true
		close(done)
		time.Sleep(5 * time.Second)
		fmt.Println("退出了三个协程")
	}()

}

func sserr() error {

	var err error

	//下面的示例 在函数中这样结束 旧版本的Go 是不会编译的，
	if err != nil {
		return err
	} else {
		return nil
	}
}

func nameretparam() (i, j int32, err error) { //命名的返回参数，初始值为 0 或 nil
	i, j = 4, 5
	err = errors.New("test error")
	return
}

func deferfunc() (ret int32) {
	ret = 1

	defer func() { //闭包 匿名函数
		ret = ret * ret //这里才是 deferfunc函数最后执行的语句，但ret的值是defer前的值，与下面代码无关，所以函数返回的是4 而不是 2
	}() //这里有个括号的意思是 定义并调用了匿名函数
	defer fmt.Printf("带defer前缀 测试1 ret值是初始化后的值 %v 与本行的其它代码无关 \n", ret)
	defer func() {
		ret = ret + 1
	}()
	defer fmt.Printf("带defer前缀 测试2 最后写的比测试1先执行 %v \n", ret)

	fmt.Printf("无defer前缀 测试 1 %v \n", ret)
	ret = ret + 1
	fmt.Printf("无defer前缀 测试 2 %v \n", ret)
	ret = ret + 10

	return 1 //这里虽然返回1，但是由于有 defer的语句修改了返回值，所以返回的是最后defer修改的值
}

func unknowinputlen(arg ...int) { //变参函数体

	fmt.Printf("变参函数体 的输入是slice类型 (%v) \n", reflect.TypeOf(arg))
	fmt.Printf("unknowinputlen 总共有 %v 个参输入 \n", len(arg))
	fmt.Println("输入 分别如下")
	for i, a := range arg {
		fmt.Printf("第 %v 个值是: %v\n", i+1, a)
	}
}

func funcasvar() {
	a := func() { //定义一个匿名函数，并且赋值给a
		println("func as var")
	} //这里没有()

	fmt.Printf("funcasvar 里面的 函数变量类型为 %v \n", reflect.TypeOf(a))
	a() //调用函数
}

func callback(f func()) {
	f()
}

func throwsPanic(f func()) (b bool) { //定义了 throwsPanic函数，里面执行函数参数 f()
	defer func() { //recover只有在defer前缀才有效
		if x := recover(); x != nil { //如果f()中Panic了，x返回就不是nil了，否则返回非nil
			b = true
			fmt.Printf("throwsPanic recover返回非空，接收到panic : %v \n", x)
		}
	}()

	f() //这里执行 f()
	return
}

type NameAge struct {
	//注意首字母大写的字段可以被导出，也就是说，在其他包中可以进行读写。
	name string //不导出
	age  int    //不导出
}

func (nameage *NameAge) print() { //为类型 NameAge 添加一个函数
	fmt.Println("我的名字是 ", nameage.name, "，我的年龄是 ", nameage.age)
}

type IPersonInfo interface { //定义一个人员信息接口
	//注意首字母大写的字段可以被导出，也就是说，在其他包中可以进行读写。
	print()
	set(string, int)
	getname() string
	getage() int
}

func (nameage *NameAge) getname() string {
	return nameage.name
}

func (nameage *NameAge) getage() int {
	return nameage.age
}

func (nameage *NameAge) set(name string, age int) {
	nameage.name = name
	nameage.age = age
}

func showItype(p IPersonInfo) { //利用switch判断接口的类型
	switch t := p.(type) {
	case *NameAge:
		fmt.Printf("p 的类型是 %v\n", t)
		fmt.Printf("t 的类型是 %v\n", reflect.TypeOf(t))
	// case NameAge:
	// 	fmt.Println(t)
	default:
		fmt.Println(t)
	}
}

func emptyInterfactest(p interface{}) {
	//IPersonInfo(p).print() //这样写是无效的
	defer func() {
		if e := recover(); e != nil {
			fmt.Println(e)
		}
	}()
	p.(IPersonInfo).print() //如果p没有实现IPersonInfo接口是会报错的
}

type S struct{ i int }

func (p S) Get() int  { return p.i }
func (p S) Put(v int) { p.i = v } //这样定义的S结构 实现了下面定义的接口

type I interface {
	Get() int
	Put(int)
	//IPersonInfo	//在接口中 列出其它的接口
}
type R struct{ i int }

func (p *R) Get() int  { return p.i }
func (p *R) Put(v int) { p.i = v }

func f(p I) {
	switch t := p.(type) {
	case *S:
	case *R:
	case S:
	// case R:	//由于R实现接口I时，指明了是指针，所以这个case不存在
	default:
		fmt.Println(t)
	}
}

type Foo int //绕开  使内建类型int 也可以带有方法

func (foo Foo) fooMethod() {
	fmt.Println("fooMethod()  foo = ", foo)
}
